﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Gasco.DataAccess;
using System.Data.Linq;
using System.Diagnostics;
using Gasco.Business.Workflows;

namespace Gasco.Business.Services
{
    public enum OrderType
    {
        Purchase = 1,
        Sale = 2
    }

    public enum PriceCalculationType
    {
        Fixed = 1,
        PlattAverage = 2
    }

    public class OrderService : IOrderService
    {
        #region IOrderService Members

        public int CountGetOrders()
        {
            var db = DatabaseHelper.GetData();
            return db.orders.Count();
        }

        public IList<order> GetAll()
        {
            var db = DatabaseHelper.GetData();
            return db.orders.ToList<order>();
        }

        public IList<order> GetOrders(string keyword, int orderType, int pageSize, int pageNumber, string sortBy, string sortDirection)
        {
            var db = DatabaseHelper.GetData();
            IQueryable<order> result = null;

            if (String.IsNullOrEmpty(keyword))
            {
                result = from o in db.orders
                         where o.order_type == orderType
                         select o;
            }
            else
            {
                result = from o in db.orders
                         where ( o.order_name.Contains(keyword) || o.order_number.Contains(keyword) || o.purchase_order.Value.ToString().Contains(keyword) ||
                                 o.product.name.Contains(keyword)) && o.order_type == orderType
                         select o;
            }

            return result.Skip((pageNumber - 1) * pageSize).Take(pageSize).SortBy<order>(sortBy).ToList<order>();
        }

        public order GetOrderById(int id)
        {
            order order = null;

            using (var db = DatabaseHelper.GetData())
            {
                db.DeferredLoadingEnabled = false;
                order = (from o in db.orders
                             where o.id == id
                             select o).FirstOrDefault<order>();
            }

            if ( order != null )
                order.SetAsChangeTrackingRoot(true);

            return order;
        }

        public IList<order> GetOrdersByPurchaseId(int purchaseOrderId)
        {
            var db = DatabaseHelper.GetData();

            var result = from o in db.orders
                         where o.purchase_order == purchaseOrderId
                         select o;

            return result.ToList<order>();
        }

        public order GetOrderByName(string name)
        {
           
            order order = null;

            var db = DatabaseHelper.GetData();
                order = (from o in db.orders
                         where o.order_name == name
                          select o).FirstOrDefault();
            
            if (order == null)
            {
                throw new OrderNameNotFoundException(name);
            }

            return order;
        }

        public order GetOrderByNumber(string number)
        {
            order order = null;

            var db = DatabaseHelper.GetData();
                order = (from o in db.orders
                         where o.order_number == number
                         select o).FirstOrDefault();
            
            if (order == null)
            {
                throw new OrderNumberNotFoundException(number);
            }

            return order;
        }

        private order GetDuplicateOrder(order o)
        {
            var db = DatabaseHelper.GetData();
            order result = (from myorder in db.orders
                     where ( myorder.order_number == o.order_number )
                           && myorder.id != o.id
                     select myorder).FirstOrDefault();

            return result;
        }

        public IList<order> AutoCompleteOrder(string name)
        {
            var db = DatabaseHelper.GetData();
            
            var result = from o in db.orders
                         where o.order_name.Contains(name)
                         select o;

            if (result.Count() == 0)
                return new List<order>();

            return result.Take(10).ToList<order>();
        }

        public IList<order> AutoCompleteOrderNum(string number)
        {
            var db = DatabaseHelper.GetData();
            var result = from o in db.orders
                         where o.order_number.Contains(number)
                         select o;

            return result.Take(10).ToList<order>();
        }


        public order CreateOrder(order o)
        {
            o.order_status_id = (int)OrderStates.Initial;
            CheckRules(o);
            return DatabaseHelper.Insert<order>(o);
        }

        public void UpdateOrder(order o)
        {
            CheckRules(o);
            DatabaseHelper.Update<order>(o);
        }

        public void DeleteOrder(order o)
        {
            if (o.order_type == (int)OrderType.Purchase)
            {
                IList<order> result = this.GetOrdersByPurchaseId(o.id);
                if (result.Count() != 0)
                {
                    throw new OrderHasSaleOrdersException();
                }
            }

            DatabaseHelper.Delete<order>(o);
        }

        public void DeleteOrder(int id)
        {
            order o = GetOrderById(id);
            if (o.order_type == (int)OrderType.Purchase)
            {
                IList<order> result = this.GetOrdersByPurchaseId(o.id);
                if (result.Count() != 0)
                {
                    throw new OrderHasSaleOrdersException();
                }
            }

            DatabaseHelper.Delete<order>(o);
        }

        private void CheckRules(order o)
        {
            if (o.order_type == (int)OrderType.Sale && !o.purchase_order.HasValue)
                throw new PurchaseOrderNotSpecifiedException();

            if (o.order_type == (int)OrderType.Sale)
            {
                order purchaseOrder = this.GetOrderById(o.purchase_order.Value);

                if (purchaseOrder == null)
                    throw new PurchaseOrderNotFoundException();

                if (o.quantity > purchaseOrder.quantity)
                    throw new InvalidQuantityException();


                var db = DatabaseHelper.GetData();
                var salesBelongToPurchase = from sales in db.orders
                                            where sales.purchase_order.Value == o.purchase_order.Value && sales.id != o.id
                                            select sales;

                if (salesBelongToPurchase.Count() != 0)
                {
                    float sumSales = salesBelongToPurchase.Sum<order>(orders => orders.quantity);
                    sumSales = sumSales + o.quantity;

                    if (sumSales > purchaseOrder.quantity)
                        throw new InvalidQuantityException();
                }

                if (o.expire_date > purchaseOrder.expire_date)
                    throw new PurchaseAndSaleExpireDateMismatch();
            }

            if (o.order_type == (int)OrderType.Purchase && o.purchase_order.HasValue)
                throw new PurchaseOrderMustBeNullException();

            if (o.expire_date < o.start_date)
                throw new ExpireAndCreateDateMismatch();

            if (o.order_type == (int)OrderType.Sale)
            {
                if (!o.border_id.HasValue || o.border_id.Value == -1)
                    throw new BorderRequiredForSaleException();
            }

            order sample = GetDuplicateOrder(o);
            if (sample != null)
                throw new DuplicateOrderException();
        }

        public IList<contract_type> GetContractTypes()
        {
            var db = DatabaseHelper.GetData();
            return db.contract_types.ToList<contract_type>();
        }

        #endregion
    }
}

